package com.pig4cloud.pigx.user.util;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.pig4cloud.pigx.common.customize.contants.CommonFieldNameConstants;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.redis.constants.RedisKeyEnum;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.user.constant.I18nMessages;
import com.pig4cloud.pigx.user.entity.UserApiConfig;
import com.pig4cloud.pigx.user.entity.UserApiIp;
import com.pig4cloud.pigx.user.entity.UserApiKey;
import com.pig4cloud.pigx.user.mapper.UserApiConfigMapper;
import com.pig4cloud.pigx.user.mapper.UserApiIpMapper;
import com.pig4cloud.pigx.user.mapper.UserApiKeyMapper;
import com.pig4cloud.pigx.user.mapstruct.UserApiIpStruct;
import com.pig4cloud.pigx.user.mapstruct.UserApiKeyStruct;
import com.pig4cloud.pigx.user.vo.UserApiConfigVO;
import com.pig4cloud.pigx.user.vo.UserApiIpCacheVO;
import com.pig4cloud.pigx.user.vo.UserApiKeyCacheVO;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 用户模块业务相关工具类
 *
 * @since 2022/10/31 11:46
 */
@Component
@AllArgsConstructor
public class UserUtil {

    private final UserApiConfigMapper userApiConfigMapper;
    private final UserApiKeyMapper userApiKeyMapper;
    private final UserApiIpMapper userApiIpMapper;

    private final ConversionService conversionService;
    private final RedisTemplate redis;
    private final CommonUtil commonUtil;
    private final SecurityUtil securityUtil;

    /**
     * 查询开放api配置
     *
     * @return {@link UserApiConfigVO}
     * @since 2022/11/1 10:06
     */
    public UserApiConfigVO getApiConfig() {
        Class<UserApiConfigVO> type = UserApiConfigVO.class;
        Field[] fields = type.getDeclaredFields();

        UserApiConfigVO vo = new UserApiConfigVO();

        List<String> fieldNameList = Arrays.stream(fields)
                .map(Field::getName)
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(fieldNameList)) {
            // 根据属性名查询配置
            QueryWrapper<UserApiConfig> query = new QueryWrapper<>();
            query.lambda()
                    .select(UserApiConfig::getName, UserApiConfig::getValue)
                    .in(UserApiConfig::getName, fieldNameList);

            List<UserApiConfig> configList = userApiConfigMapper.selectList(query);

            // 遍历，并设置属性
            configList.forEach(config -> {
                for (Field field : fields) {
                    if (Objects.equals(config.getName(), field.getName())) {
                        // 转换字符串到对应的类型
                        if (!conversionService.canConvert(String.class, field.getType())) {
                            throw new RuntimeException("无法找到对应的converter");
                        }
                        Object value = conversionService.convert(config.getValue(), field.getType());

                        try {
                            field.setAccessible(true);
                            field.set(vo, value);
                            field.setAccessible(false);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                            throw new RuntimeException("服务异常，请联系管理员");
                        }
                    }
                }
            });
        }

        return vo;
    }

    /**
     * 将 UserApiConfigVO 转换为 List<UserApiConfig>
     *
     * @param vo vo
     * @return {@link List< UserApiConfig>}
     * @since 2022/11/1 10:33
     */
    public List<UserApiConfig> apiConfigVO2List(UserApiConfigVO vo) {
        PigxUser user = SecurityUtil.getLoginUserNotNull();
        LocalDateTime now = LocalDateTime.now();

        Class<UserApiConfigVO> type = UserApiConfigVO.class;
        Field[] fields = type.getDeclaredFields();

        // 转换到实体
        List<UserApiConfig> list = new ArrayList<>(fields.length);
        for (Field field : fields) {
            // 序列化id排除
            if (CommonFieldNameConstants.SERIAL_VERSION_UID.equals(field.getName())) {
                continue;
            }

            try {
                // 打破封装
                field.setAccessible(true);

                UserApiConfig config = new UserApiConfig();
                config.setName(field.getName());
                config.setValue(String.valueOf(field.get(vo)));
                config.setCreateName(user.getName());
                config.setUpdateName(user.getName());
                securityUtil.initCreateAbout(config, user, now);
                list.add(config);

                // 重新封装
                field.setAccessible(false);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new RuntimeException("服务异常，请联系管理员");
            }
        }

        return list;
    }

    /**
     * 更新redis中的api配置缓存
     *
     * @param config 配置
     * @since 2022/11/1 10:14
     */
    public void flushRedisApiConfig(UserApiConfigVO config) {
        redis.opsForHash().put(RedisKeyEnum.ConfigHashKey.getKey(), RedisKeyEnum.UserApi.getKey(), config);
    }

    /**
     * 获取redis中的api配置
     *
     * @return {@link UserApiConfigVO}
     * @since 2022/11/1 10:14
     */
    public UserApiConfigVO getApiConfigCache() {
        Object obj = redis.opsForHash().get(RedisKeyEnum.ConfigHashKey.getKey(), RedisKeyEnum.UserApi.getKey());

        if (Objects.isNull(obj) || !(obj instanceof UserApiConfigVO)) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.SERVICE_ABNORMAL));
        }

        return (UserApiConfigVO) obj;
    }

    /**
     * 开放Api-获取AccessKey在Redis中的key
     * <p> user_api:access_key:{accessKey}
     *
     * @param accessKey AccessKey
     * @return {@link String}
     * @since 2022/11/3 17:03
     */
    public static String getApiAccessKeyRedisKey(String accessKey) {
        return String.format(RedisKeyEnum.UserApiAccessKey.getKey(), accessKey);
    }

    /**
     * 开放Api-获取AccessKey的IP在Redis中的key
     * <p> user_api:ip:{apiKeyId}
     *
     * @param accessKey accessKey
     * @return {@link String}
     * @since 2022/11/3 17:03
     */
    public static String getApiIpRedisKey(String accessKey) {
        return String.format(RedisKeyEnum.UserApiIp.getKey(), accessKey);
    }

    /**
     * 开放Api-根据id/accessKey查询最新数据以刷新AccessKey的redis缓存，并返回缓存数据
     *
     * @param accessKey 访问密钥
     * @param id        id
     * @return {@link UserApiKeyCacheVO}
     * @since 2022/11/3 17:24
     */
    public UserApiKeyCacheVO flushRedisApiAccessKey(@Nullable String accessKey,
                                                    @Nullable Long id) {
        if (Objects.isNull(id) && Strings.isBlank(accessKey)) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.SERVICE_ABNORMAL));
        }

        QueryWrapper<UserApiKey> query = new QueryWrapper<>();
        query.lambda()
                .select(UserApiKey::getId,
                        UserApiKey::getUserId,
                        UserApiKey::getName,
                        UserApiKey::getAccessKey,
                        UserApiKey::getSecretKey,
                        UserApiKey::getPermission,
                        UserApiKey::getHasIpLimit,
                        UserApiKey::getStatus)
                .eq(!Objects.isNull(id), UserApiKey::getId, id)
                .eq(Strings.isNotBlank(accessKey), UserApiKey::getAccessKey, accessKey)
                .eq(UserApiKey::getIsDeleted, CommonDictEnum.IsDeleted.NO.getValue());
        UserApiKey entity = userApiKeyMapper.selectOne(query);

        return flushRedisApiAccessKey(entity);
    }

    /**
     * 开放Api-刷新AccessKey的redis缓存，并返回缓存数据
     *
     * @param entity entity
     * @return {@link UserApiKeyCacheVO}
     * @since 2022/11/3 17:24
     */
    public UserApiKeyCacheVO flushRedisApiAccessKey(UserApiKey entity) {
        // 转换到CacheVo
        UserApiKeyCacheVO vo = UserApiKeyStruct.INSTANCE.entity2CacheVO(entity);
        RedisKeyEnum keyConfig = RedisKeyEnum.UserApiAccessKey;
        redis.opsForValue().set(getApiAccessKeyRedisKey(entity.getAccessKey()),
                vo,
                keyConfig.getExpire(),
                keyConfig.getExpireUnit());

        return vo;
    }

    /**
     * 开放Api-根据apiKeyId查询最新数据以刷新IP的redis缓存，并返回缓存数据
     *
     * @param apiKeyId  apiKeyId
     * @param accessKey accessKey
     * @return {@link List< UserApiIpCacheVO>}
     * @since 2022/11/3 17:24
     */
    public List<UserApiIpCacheVO> flushRedisApiIp(Long apiKeyId,
                                                  @Nullable String accessKey) {
        if (Objects.isNull(apiKeyId)) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.SERVICE_ABNORMAL));
        }

        // 查询AccessKey
        if (Strings.isBlank(accessKey)) {
            UserApiKey apiKey = userApiKeyMapper.selectById(apiKeyId);
            if (Objects.isNull(apiKey)) {
                throw new RuntimeException(MessageUtils.get(I18nMessages.SERVICE_ABNORMAL));
            }

            accessKey = apiKey.getAccessKey();
        }


        QueryWrapper<UserApiIp> query = new QueryWrapper<>();
        query.lambda()
                .select(UserApiIp::getId,
                        UserApiIp::getAddress,
                        UserApiIp::getStatus)
                .eq(UserApiIp::getApiKeyId, apiKeyId)
                .eq(UserApiIp::getIsDeleted, CommonDictEnum.IsDeleted.NO.getValue());
        List<UserApiIp> list = userApiIpMapper.selectList(query);

        return flushRedisApiIp(list, accessKey);
    }

    /**
     * 开放Api-刷新IP的redis缓存，并返回缓存数据
     *
     * @param list      list
     * @param accessKey accessKey
     * @return {@link List< UserApiIpCacheVO>}
     * @since 2022/11/3 17:24
     */
    public List<UserApiIpCacheVO> flushRedisApiIp(List<UserApiIp> list, String accessKey) {
        // 转换到CacheVo
        List<UserApiIpCacheVO> voList = UserApiIpStruct.INSTANCE.list2CacheVOList(list);
        RedisKeyEnum keyConfig = RedisKeyEnum.UserApiIp;
        redis.opsForValue().set(getApiIpRedisKey(accessKey),
                voList,
                keyConfig.getExpire(),
                keyConfig.getExpireUnit());

        return voList;
    }

    /**
     * 根据AccessKey查询信息
     * <p>1、从redis缓存获取
     * <p>2、如果redis中没有，从mysql获取，并缓存到redis中
     *
     * @param accessKey 访问密钥
     * @return {@link UserApiKeyCacheVO}
     * @since 2022/11/9 11:13
     */
    public UserApiKeyCacheVO getApiKeyCache(String accessKey) {
        String key = getApiAccessKeyRedisKey(accessKey);
        // 如果redis中有缓存，查询出来直接返回
        if (redis.hasKey(key)) {
            Object cache = redis.opsForValue().get(key);
            if (Objects.isNull(cache)) {
                return null;
            } else {
                if (!(cache instanceof UserApiKeyCacheVO)) {
                    return null;
                }

                return (UserApiKeyCacheVO) cache;
            }
        }

        // 查询mysql、并缓存到redis中
        return flushRedisApiAccessKey(accessKey, null);
    }

    /**
     * 根据AccessKey查询相关IP信息
     *
     * @param apiKeyId  apiKeyId
     * @param accessKey accessKey
     * @return {@link List< UserApiIpCacheVO>}
     * @since 2022/11/9 11:27
     */
    public List<UserApiIpCacheVO> getApiKeyIpCache(Long apiKeyId, String accessKey) {
        String key = getApiIpRedisKey(accessKey);

        // 如果redis中有缓存，查询出来直接返回
        if (redis.hasKey(key)) {
            Object cache = redis.opsForValue().get(key);
            if (Objects.isNull(cache)) {
                return null;
            } else {
                if (!(cache instanceof List)) {
                    return null;
                }

                return (List<UserApiIpCacheVO>) cache;
            }
        }

        // 查询mysql、并缓存到redis中
        return flushRedisApiIp(apiKeyId, accessKey);
    }
}
